using System;
using System.Xml.Serialization;
using System.Text.RegularExpressions;
using Wintellect.PowerCollections;

namespace Pylor
{
    [Serializable]
    [XmlType(AnonymousType = true, Namespace = Rules.Namespace)]
    public class Rule
    {
        internal static Set<string> CreateRoleSet()
        {
            return new Set<string>(StringComparer.InvariantCultureIgnoreCase);
        }

        private const string Everyone = "*";

        [XmlAttribute(AttributeName = "roles")]
        public string Roles
        {
            get { return rolesRaw; } 
            set
            {
                if (value == null)
                    throw new ArgumentNullException("value");

                rolesRaw = value.Trim();
                roles = CreateRoleSet();
                var rs = Roles.Split(',');

                for (var i = 0; i < rs.Length; i++)
                {
                    var r = rs[i].Trim();
                    roles.Add(r);
                }
            }
        }
        
        [XmlAttribute(AttributeName = "resource")]
        public string Resource
        {
            get { return resource; }
            set 
            { 
                if (value == null)
                    throw new ArgumentNullException("value");

                resource = value;
                resourceRegex = new Regex(resource, RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.IgnoreCase);
            }
        }
        
        [XmlAttribute(AttributeName = "permission")]
        public Permission Permission { get; set; }

        /// <summary>
        /// Returns "Default" if the rule is not applicable to the provided resource.
        /// </summary>
        public Permission HasPermission(Set<string> rolesArg, string resourceArg)
        {
            if (rolesArg == null)
                throw new ArgumentNullException("rolesArg");
            if (string.IsNullOrEmpty(resourceArg))
                throw new ArgumentNullException("resourceArg");

            if (!IsResourceMatch(resourceArg))
                return Permission.Default;

            if (!IsAnyRoleMatch(rolesArg))
                return Permission.Default;

            return Permission;
        }

        private bool IsAnyRoleMatch(Set<string> rs)
        {
            if (string.Compare(rolesRaw, Everyone) == 0)
                return true;

            var intersection = roles.Intersection(rs);
            return intersection.Count > 0;
        }

        private bool IsResourceMatch(string s)
        {
            return resourceRegex.IsMatch(s);
        }

        private string resource;
        private string rolesRaw;
        private Set<string> roles;
        private Regex resourceRegex;
    }
}